unit heContnrs_Vector_test;

{$mode objfpc}{$H+}

interface

uses
  Classes,
  fpcunit,
  heContnrs;

type

  TVectorInt = type Integer;

  { TTestVectorInt }

  _TTestVectorInt = specialize TheVector<TVectorInt>;
  TTestVectorInt = class(_TTestVectorInt)
  private
    FID: Integer;
    FTracer: TBits;
  public
    constructor Create(const ATracer: TBits; const AID: Integer);
    destructor Destroy; override;
    property ID: Integer read FID;
    property Tracer: TBits read FTracer;
  end;

  TVectorStr = type String;

  { TTestVectorStr }

  _TTestVectorStr = specialize TheVector<TVectorStr>;
  TTestVectorStr = class(_TTestVectorStr)
  private
    FID: Integer;
    FTracer: TBits;
  public
    constructor Create(const ATracer: TBits; const AID: Integer);
    destructor Destroy; override;
    property ID: Integer read FID;
    property Tracer: TBits read FTracer;
  end;

  { TTestCmpVectorInt }

  _TTestCmpVectorInt = specialize TheCmpVector<Integer>;
  TTestCmpVectorInt = class(_TTestCmpVectorInt)
  private
    FID: Integer;
    FTracer: TBits;
  public
    constructor Create(const ATracer: TBits; const AID: Integer);
    destructor Destroy; override;
    function Compare(const A, B: Integer): Integer; override;
    property ID: Integer read FID;
    property Tracer: TBits read FTracer;
  end;

  { TTestCmpVectorStr }

  _TTestCmpVectorStr = specialize TheCmpVector<String>;
  TTestCmpVectorStr = class(_TTestCmpVectorStr)
  private
    FID: Integer;
    FTracer: TBits;
  public
    constructor Create(const ATracer: TBits; const AID: Integer);
    destructor Destroy; override;
    function Compare(const A, B: String): Integer; override;
    property ID: Integer read FID;
    property Tracer: TBits read FTracer;
  end;

  { TTestCmpVectorInterface }

  _TTestCmpVectorInterface = specialize TheCmpVector<IInterface>;
  TTestCmpVectorInterface = class(_TTestCmpVectorInterface)
  private
    FID: Integer;
    FTracer: TBits;
  public
    constructor Create(const ATracer: TBits; const AID: Integer);
    destructor Destroy; override;
    function Compare(const A, B: IInterface): Integer; override;
    property ID: Integer read FID;
    property Tracer: TBits read FTracer;
  end;

  { TTestObjectVector }

  _TTestObjectVector = specialize TheObjectVector<TObject>;
  TTestObjectVector = class(_TTestObjectVector)
  private
    FID: Integer;
    FTracer: TBits;
  public
    constructor Create(const ATracer: TBits; const AID: Integer);
    destructor Destroy; override;
    procedure Sort;
    property ID: Integer read FID;
    property Tracer: TBits read FTracer;
  end;

  { TheVectorTest }

  generic TheVectorTest<TVector, TItem> = class(TTestCase)
  private
    Vector: TVector;
  protected
    function Item(const N: Integer): TItem; virtual; // abstract;
    function Value(const It: TItem): Integer; virtual; // abstract;
    procedure Done(var It: TItem); virtual;
    procedure SetUp; override;
    procedure TearDown; override;
  published
    procedure TestCreate;
    procedure TestAdd0;
    procedure TestAdd2;
    procedure TestAdd3;
    procedure TestPush;
    //procedure TestGetEnumerator;
    //procedure TestReversed;
    procedure TestClear;
    procedure TestPack;
    procedure TestGetItems;
    procedure TestSetItems;
    procedure TestSwap;
    procedure TestSwapWith;
    procedure TestSort;
    procedure TestExtract;
    procedure TestDelete;
    procedure TestIndexOf;
    procedure TestFind;
    procedure TestHas;
    procedure TestInsert;
    procedure TestFirst;
    procedure TestLast;
    procedure TestRemove;
  end;

  { TheIntVectorTest }

  _TheIntVectorTest = specialize TheVectorTest<TTestVectorInt, Integer>;
  TheIntVectorTest = class(_TheIntVectorTest)
  protected
    function Item(const N: Integer): Integer; override;
    function Value(const It: Integer): Integer; override;
  published
    procedure TestGetEnumerator;
    procedure TestReversed;
  end;

  { TheStrVectorTest }

  _TheStrVectorTest = specialize TheVectorTest<TTestVectorStr, String>;
  TheStrVectorTest = class(_TheStrVectorTest)
  protected
    function Item(const N: Integer): String; override;
    function Value(const It: String): Integer; override;
  published
    procedure TestGetEnumerator;
    procedure TestReversed;
  end;

  { TheIntCmpVectorTest }

  _TheIntCmpVectorTest = specialize TheVectorTest<TTestCmpVectorInt, Integer>;
  TheIntCmpVectorTest = class(_TheIntCmpVectorTest)
  protected
    function Item(const N: Integer): Integer; override;
    function Value(const It: Integer): Integer; override;
  published
    procedure TestGetEnumerator;
    procedure TestReversed;
  end;

  { TheStrCmpVectorTest }

  _TheStrCmpVectorTest = specialize TheVectorTest<TTestCmpVectorStr, String>;
  TheStrCmpVectorTest = class(_TheStrCmpVectorTest)
  protected
    function Item(const N: Integer): String; override;
    function Value(const It: String): Integer; override;
  published
    procedure TestGetEnumerator;
    procedure TestReversed;
  end;

  { TheInterfaceCmpVectorTest }

  _TheInterfaceCmpVectorTest = specialize TheVectorTest<TTestCmpVectorInterface, IInterface>;
  TheInterfaceCmpVectorTest = class(_TheInterfaceCmpVectorTest)
  protected
    function Item(const N: Integer): IInterface; override;
    function Value(const It: IInterface): Integer; override;
  published
    procedure TestGetEnumerator;
    procedure TestReversed;
  end;

  { TheObjectVectorTest }

  _TheObjectVectorTest = specialize TheVectorTest<TTestObjectVector, TObject>;
  TheObjectVectorTest = class(_TheObjectVectorTest)
  protected
    function Item(const N: Integer): TObject; override;
    function Value(const It: TObject): Integer; override;
    procedure Done(var It: TObject); override;
  published
    procedure TestAdd1;
    procedure TestKill;
    procedure TestWipe;
    procedure TestGetEnumerator;
    procedure TestReversed;
  end;

implementation

uses
  SysUtils,
  testregistry,
  heContnrs_test;

function I2S(const N: Integer): String;
begin
  if N < 0 then
    Result := Format('N%.8x', [N])
  else
    Result := Format('P%.8x', [N]);
end;

function S2I(const S: String): Integer;
begin
  Result := StrToInt('$' + Copy(S, 2, Length(S)));
end;

function CompareInt(const A, B: TObject): Integer;
begin
  Result := (A as TTracedObject).ID - (B as TTracedObject).ID;
end;

{ TTestVectorInt }

constructor TTestVectorInt.Create(const ATracer: TBits; const AID: Integer);
begin
  inherited Create;
  FTracer := ATracer;
  FID := AID;
  if Tracer <> nil then
    Tracer[ID] := True;
end;

destructor TTestVectorInt.Destroy;
begin
  if Tracer <> nil then
    Tracer[ID] := False;
  inherited Destroy;
end;

{ TTestVectorStr }

constructor TTestVectorStr.Create(const ATracer: TBits; const AID: Integer);
begin
  inherited Create;
  FTracer := ATracer;
  FID := AID;
  if Tracer <> nil then
    Tracer[ID] := True;
end;

destructor TTestVectorStr.Destroy;
begin
  if Tracer <> nil then
    Tracer[ID] := False;
  inherited Destroy;
end;

{ TTestCmpVectorInt }

constructor TTestCmpVectorInt.Create(const ATracer: TBits; const AID: Integer);
begin
  inherited Create;
  FTracer := ATracer;
  FID := AID;
  if Tracer <> nil then
    Tracer[ID] := True;
end;

destructor TTestCmpVectorInt.Destroy;
begin
  if Tracer <> nil then
    Tracer[ID] := False;
  inherited Destroy;
end;

function TTestCmpVectorInt.Compare(const A, B: Integer): Integer;
begin
  Result := A - B;
end;

{ TTestCmpVectorStr }

constructor TTestCmpVectorStr.Create(const ATracer: TBits; const AID: Integer);
begin
  inherited Create;
  FTracer := ATracer;
  FID := AID;
  if Tracer <> nil then
    Tracer[ID] := True;
end;

destructor TTestCmpVectorStr.Destroy;
begin
  if Tracer <> nil then
    Tracer[ID] := False;
  inherited Destroy;
end;

function TTestCmpVectorStr.Compare(const A, B: String): Integer;
begin
  Result := SysUtils.CompareStr(A, B);
end;

{ TTestCmpVectorInterface }

constructor TTestCmpVectorInterface.Create(const ATracer: TBits; const AID: Integer);
begin
  inherited Create;
  FTracer := ATracer;
  FID := AID;
  if Tracer <> nil then
    Tracer[ID] := True;
end;

destructor TTestCmpVectorInterface.Destroy;
begin
  if Tracer <> nil then
    Tracer[ID] := False;
  inherited Destroy;
end;

function TTestCmpVectorInterface.Compare(const A, B: IInterface): Integer;
begin
  if (A = nil) then
    if B = nil then
      Exit(0)
    else
      Exit(1)
  else if B = nil then
    if A = nil then
      Exit(0)
    else
      Exit(-1);
  Result := (A as TInterfacedTracedObject).ID - (B as TInterfacedTracedObject).ID;
end;

{ TTestObjectVector }

constructor TTestObjectVector.Create(const ATracer: TBits; const AID: Integer);
begin
  inherited Create(True);
  FTracer := ATracer;
  FID := AID;
  if Tracer <> nil then
    Tracer[ID] := True;
end;

destructor TTestObjectVector.Destroy;
begin
  if Tracer <> nil then
    Tracer[ID] := False;
  inherited Destroy;
end;

procedure TTestObjectVector.Sort;
begin
  inherited Sort(@CompareInt);
end;

{ TheVectorTest }

function TheVectorTest.Item(const N: Integer): TItem;
begin
  Assert(N = N); // hint off
  Initialize(Result); // hint off
  raise EAbstractError.Create(Format('%s.Item', [ClassName]));
end;

function TheVectorTest.Value(const It: TItem): Integer;
begin
  Assert(@It = @It); // hint off
  Initialize(Result); // hint off
  raise EAbstractError.Create(Format('%s.Value', [ClassName]));
end;

procedure TheVectorTest.Done(var It: TItem);
begin
  Assert(@It = @It); // hint off
end;

procedure TheVectorTest.SetUp;
begin
  Vector := TVector.Create(nil, 0);
end;

procedure TheVectorTest.TearDown;
begin
  FreeAndNil(Vector);
end;

procedure TheVectorTest.TestCreate;
begin
  AssertEquals('Initial .Count', 0, Vector.Count);
end;

procedure TheVectorTest.TestAdd0;
begin
  AssertEquals('.Add', 0, Vector.Add(Item(0)));
  AssertEquals('.Count', 1, Vector.Count);
end;

procedure TheVectorTest.TestAdd2;
const N = 10;
var I: Integer;
begin
  for I := 1 to N do begin
    Vector.Capacity := 0;
    AssertTrue('.Capacity >= 0', Vector.Capacity >= 0);
    AssertEquals('.Add', I - 1, Vector.Add(Item(42)));
    AssertEquals('.Count', I, Vector.Count);
    AssertTrue('.Capacity > 0', Vector.Capacity > 0);
  end;
end;

procedure TheVectorTest.TestAdd3;
const N = 10;
var
  I: Integer;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    Vector.Capacity := 0;
    AssertTrue('.Capacity >= 0', Vector.Capacity >= 0);
    A[I] := Item(I);
    AssertEquals('a) .Add', I, Vector.Add(A[I]));
    AssertEquals('b) .Add', Value(A[I]), Value(Vector[I]));
    AssertEquals('.Count', I + 1, Vector.Count);
    AssertTrue('.Capacity > 0', Vector.Capacity > 0);
  end;
end;

procedure TheVectorTest.TestPush;
const N = 10;
var
  I: Integer;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    Vector.Capacity := 0;
    AssertTrue('.Capacity >= 0', Vector.Capacity >= 0);
    A[I] := Item(10 * I);
    AssertEquals('.Push', Value(A[I]), Value(Vector.Push(A[I])));
    AssertEquals('V[I]', Value(A[I]), Value(Vector[I]));
    AssertEquals('.Count', I + 1, Vector.Count);
    AssertTrue('.Capacity > 0', Vector.Capacity > 0);
  end;
end;

procedure TheVectorTest.TestClear;
const N = 10;
var I: Integer;
begin
  for I := 0 to N - 1 do
    Vector.Add(Item(I));
  Vector.Clear;
  AssertEquals('.Count', 0, Vector.Count);
end;

procedure TheVectorTest.TestPack;
const N = 11;
var I: Integer;
begin
  for I := 0 to N - 1 do
    Vector.Add(Item(I));
  Vector.Capacity := 2 * N;
  AssertEquals('a) .Capacity', 2 * N, Vector.Capacity);
  Vector.Pack;
  AssertEquals('b) .Capacity', Vector.Count, Vector.Capacity);
end;

procedure TheVectorTest.TestGetItems;
const N = 10;
var
  I, J: Integer;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
    for J := 0 to I do
      AssertEquals('V', Value(A[J]), Value(Vector[J]));
  end;
end;

procedure TheVectorTest.TestSetItems;
const N = 10;
var
  I: Integer;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do
    Vector.Add(Item(-1));
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector[I] := A[I];
  end;
  for I := 0 to N - 1 do
    AssertEquals('V', Value(A[I]), Value(Vector[I]));
end;

procedure TheVectorTest.TestSwap;
var
  A: array[0..3] of TItem;
  I: Integer;
begin
  for I := Low(A) to High(A) do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
  end;
  Vector.Swap(1, 2);
  AssertEquals('V[0]', Value(A[0]), Value(Vector[0]));
  AssertEquals('V[1]', Value(A[2]), Value(Vector[1]));
  AssertEquals('V[2]', Value(A[1]), Value(Vector[2]));
  AssertEquals('V[3]', Value(A[3]), Value(Vector[3]));
end;

procedure TheVectorTest.TestSwapWith;
var
  A: array[0..2] of TItem;
  I: Integer;
  V: TItem;
begin
  for I := Low(A) to High(A) do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
  end;
  V := Vector.SwapWith(1, Item(-1));
  AssertEquals('V', Value(A[1]), Value(V));
  Done(V);
  AssertEquals('V[0]', Value(A[0]), Value(Vector[0]));
  AssertEquals('V[1]', -1, Value(Vector[1]));
  AssertEquals('V[2]', Value(A[2]), Value(Vector[2]));
end;

procedure TheVectorTest.TestSort;
var
  A: array[0..4] of TItem;
  I: Integer;
begin
  for I := Low(A) to High(A) do begin
    A[I] := Item(I xor $A);
    Vector.Add(A[I]);
  end;
  // 0 A
  // 1 B
  // 2 8
  // 3 9
  // 4 E
  Vector.Sort;
  AssertEquals('V[0]', Value(A[2]), Value(Vector[0]));
  AssertEquals('V[1]', Value(A[3]), Value(Vector[1]));
  AssertEquals('V[2]', Value(A[0]), Value(Vector[2]));
  AssertEquals('V[3]', Value(A[1]), Value(Vector[3]));
  AssertEquals('V[4]', Value(A[4]), Value(Vector[4]));
end;

procedure TheVectorTest.TestExtract;
var It, It2: TItem;
begin
  try
    Vector.Add(Item(0));
    It := Vector.Extract(0);
    AssertEquals('.Count', 0, Vector.Count);
    AssertEquals('It', 0, Value(It));
  finally
    Done(It);
    TearDown;
  end;

  Setup;
  try
    Vector.Add(Item(0));
    Vector.Add(Item(1));
    It := Vector.Extract(0);
    AssertEquals('b) .Count', 1, Vector.Count);
    AssertEquals('b) It', 0, Value(It));
    It2 := Vector[0];
    AssertEquals('b2) It', 1, Value(It2));
  finally
    Done(It);
    TearDown;
  end;

  Setup;
  try
    Vector.Add(Item(0));
    Vector.Add(Item(1));
    It := Vector.Extract(1);
    AssertEquals('c) .Count', 1, Vector.Count);
    AssertEquals('c) It', 1, Value(It));
    It2 := Vector[0];
    AssertEquals('c2) It', 0, Value(It2));
  finally
    Done(It);
    TearDown;
  end;

  Setup;
  try
    Vector.Add(Item(0));
    Vector.Add(Item(1));
    Vector.Add(Item(2));
    It := Vector.Extract(1);
    AssertEquals('d) .Count', 2, Vector.Count);
    AssertEquals('d) It', 1, Value(It));
    It2 := Vector[0];
    AssertEquals('d2) It', 0, Value(It2));
    It2 := Vector[1];
    AssertEquals('d3) It', 2, Value(It2));
  finally
    Done(It);
    TearDown;
  end;
end;

procedure TheVectorTest.TestDelete;
var It, It2: TItem;
begin
  It := Item(0);
  try
    Vector.Add(It);
    Vector.Delete(0);
    AssertEquals('.Count', 0, Vector.Count);
  finally
    Done(It);
    TearDown;
  end;

  Setup;
  Try
    It := Item(0);
    Vector.Add(It);
    Vector.Add(Item(1));
    Vector.Delete(0);
    AssertEquals('b) .Count', 1, Vector.Count);
    It2 := Vector[0];
    AssertEquals('b2) It', 1, Value(It2));
  finally
    Done(It);
    TearDown;
  end;

  Setup;
  try
    It := Item(1);
    Vector.Add(Item(0));
    Vector.Add(It);
    Vector.Delete(1);
    AssertEquals('c) .Count', 1, Vector.Count);
    It2 := Vector[0];
    AssertEquals('c2) It', 0, Value(It2));
  finally
    Done(It);
    TearDown;
  end;

  Setup;
  try
    It := Item(1);
    Vector.Add(Item(0));
    Vector.Add(It);
    Vector.Add(Item(2));
    Vector.Delete(1);
    AssertEquals('d) .Count', 2, Vector.Count);
    It2 := Vector[0];
    AssertEquals('d2) It', 0, Value(It2));
    It2 := Vector[1];
    AssertEquals('d3) It', 2, Value(It2));
  finally
    Done(It);
    TearDown;
  end;
end;

procedure TheVectorTest.TestIndexOf;
var Item0, Item1: TItem;
begin
  AssertTrue('.IndexOf', Vector.IndexOf(Item(-1)) < 0);
  Item0 := Item(100);
  Vector.Add(Item0);
  AssertTrue('b) .IndexOf', Vector.IndexOf(Item(-1)) < 0);
  AssertEquals('c) .IndexOf', 0, Vector.IndexOf(Item0));
  Item1 := Item(101);
  Vector.Add(Item1);
  AssertTrue('d) .IndexOf', Vector.IndexOf(Item(-1)) < 0);
  AssertEquals('e) .IndexOf', 0, Vector.IndexOf(Item0));
  AssertEquals('f) .IndexOf', 1, Vector.IndexOf(Item1));
  Assert(@Item0 = @Item0); // hint off
  Assert(@Item1 = @Item1); // hint off
end;

procedure TheVectorTest.TestFind;
var
  Item0, Item1: TItem;
  Index: Integer = 0; // hint off
begin
  AssertFalse('.Find', Vector.Find(Item(-1), Index));
  Item0 := Item(100);
  Vector.Add(Item0);
  AssertFalse('b) .Find', Vector.Find(Item(-1), Index));
  AssertTrue('c) .Find', Vector.Find(Item0, Index));
  AssertEquals('d) .Find', 0, Index);
  Item1 := Item(101);
  Vector.Add(Item1);
  AssertFalse('e) .Find', Vector.Find(Item(-1), Index));
  AssertTrue('f) .Find', Vector.Find(Item0, Index));
  AssertEquals('g) .Find', 0, Index);
  AssertTrue('h) .Find', Vector.Find(Item1, Index));
  AssertEquals('i) .Find', 1, Index);
  Assert(@Item0 = @Item0); // hint off
  Assert(@Item1 = @Item1); // hint off
end;

procedure TheVectorTest.TestHas;
var Item0, Item1: TItem;
begin
  AssertFalse('.Has', Vector.Has(Item(-1)));
  Item0 := Item(100);
  Vector.Add(Item0);
  AssertFalse('b) .Has', Vector.Has(Item(-1)));
  AssertTrue('c) .Has', Vector.Has(Item0));
  Item1 := Item(101);
  Vector.Add(Item1);
  AssertFalse('e) .Has', Vector.Has(Item(-1)));
  AssertTrue('f) .Has', Vector.Has(Item0));
  AssertTrue('h) .Has', Vector.Has(Item1));
  Assert(@Item0 = @Item0); // hint off
  Assert(@Item1 = @Item1); // hint off
end;

procedure TheVectorTest.TestRemove;
var
  It, It2: TItem;
  Index: Integer;
begin
  try
    AssertTrue('a1) .Remove < 0', Vector.Remove(Item(-1)) < 0);
    It := Item(0);
    Vector.Add(It);
    AssertTrue('b1) .Remove < 0', Vector.Remove(Item(-1)) < 0);
    Index := Vector.Remove(It);
    AssertEquals('c1) .Remove', 0, Index);
    AssertTrue('d1) .Remove < 0', Vector.Remove(Item(-1)) < 0);
    AssertEquals('e1) .Count', 0, Vector.Count);
  finally
    Done(It);
    TearDown;
  end;

  SetUp;
  try
    It := Item(0);
    Vector.Add(It);
    Vector.Add(Item(1));
    AssertTrue('a2) .Remove < 0', Vector.Remove(Item(-1)) < 0);
    Index := Vector.Remove(It);
    AssertEquals('b2) .Remove', 0, Index);
    AssertEquals('c2) .Count', 1, Vector.Count);
    It2 := Vector[0];
    AssertEquals('d2) It', 1, Value(It2));
  finally
    Done(It);
    TearDown;
  end;

  Setup;
  try
    It := Item(1);
    Vector.Add(Item(0));
    Vector.Add(It);
    Index := Vector.Remove(It);
    AssertEquals('a3) .Remove', 1, Index);
    AssertEquals('b3) .Count', 1, Vector.Count);
    It2 := Vector[0];
    AssertEquals('c3) It', 0, Value(It2));
  finally
    Done(It);
    TearDown;
  end;

  Setup;
  try
    It := Item(1);
    Vector.Add(Item(0));
    Vector.Add(It);
    Vector.Add(Item(2));
    Index := Vector.Remove(It);
    AssertEquals('a4) .Remove', 1, Index);
    AssertEquals('b4) .Count', 2, Vector.Count);
    It2 := Vector[0];
    AssertEquals('c4) It', 0, Value(It2));
    It2 := Vector[1];
    AssertEquals('d4) It', 2, Value(It2));
  finally
    Done(It);
    TearDown;
  end;
end;

procedure TheVectorTest.TestInsert;
var ItemA, ItemB, ItemC, ItemD, ItemE: TItem;
begin
  ItemA := Item(100);
  Vector.Insert(0, ItemA);
  AssertEquals('a1( .Count', 1, Vector.Count);
  AssertEquals('b1) V[0]', Value(ItemA), Value(Vector[0]));

  ItemB := Item(200);
  Vector.Insert(0, ItemB);
  AssertEquals('a2) .Count', 2, Vector.Count);
  AssertEquals('b2) V[0]', Value(ItemB), Value(Vector[0]));
  AssertEquals('c2) V[1]', Value(ItemA), Value(Vector[1]));

  ItemC := Item(300);
  Vector.Insert(2, ItemC);
  AssertEquals('a3) .Count', 3, Vector.Count);
  AssertEquals('b3) V[0]', Value(ItemB), Value(Vector[0]));
  AssertEquals('c3) V[1]', Value(ItemA), Value(Vector[1]));
  AssertEquals('d3) V[2]', Value(ItemC), Value(Vector[2]));

  ItemD := Item(400);
  Vector.Insert(1, ItemD);
  AssertEquals('a4) .Count', 4, Vector.Count);
  AssertEquals('b4) V[0]', Value(ItemB), Value(Vector[0]));
  AssertEquals('c4) V[1]', Value(ItemD), Value(Vector[1]));
  AssertEquals('d4) V[2]', Value(ItemA), Value(Vector[2]));
  AssertEquals('e4) V[3]', Value(ItemC), Value(Vector[3]));

  ItemE := Item(500);
  Vector.Insert(3, ItemE);
  AssertEquals('a5) .Count', 5, Vector.Count);
  AssertEquals('b5) V[0]', Value(ItemB), Value(Vector[0]));
  AssertEquals('c5) V[1]', Value(ItemD), Value(Vector[1]));
  AssertEquals('d5) V[2]', Value(ItemA), Value(Vector[2]));
  AssertEquals('e5) V[3]', Value(ItemE), Value(Vector[3]));
  AssertEquals('f5) V[4]', Value(ItemC), Value(Vector[4]));
end;

procedure TheVectorTest.TestFirst;
var ItemA, ItemB: TItem;
begin
  ItemA := Item(100);
  Vector.Add(ItemA);
  AssertEquals('a1) .First', Value(ItemA), Value(Vector.First));
  ItemB := Item(200);
  Vector.Add(ItemB);
  AssertEquals('a2) .First', Value(ItemA), Value(Vector.First));
  Assert(@ItemB = @ItemB); // hint off
end;

procedure TheVectorTest.TestLast;
var ItemA, ItemB: TItem;
begin
  ItemA := Item(100);
  Vector.Add(ItemA);
  AssertEquals('a1) .Last', Value(ItemA), Value(Vector.Last));
  ItemB := Item(200);
  Vector.Add(ItemB);
  AssertEquals('a2) .Last', Value(ItemB), Value(Vector.Last));
end;

{ TheIntVectorTest }

function TheIntVectorTest.Item(const N: Integer): Integer;
begin
  Result := N;
end;

function TheIntVectorTest.Value(const It: Integer): Integer;
begin
  Result := It;
end;

procedure TheIntVectorTest.TestGetEnumerator;
const N = 10;
var
  I, J: Integer;
  V: TItem;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
    J := 0;
    for V in Vector do begin
      AssertEquals('V', A[J], V);
      Inc(J);
    end;
  end;
end;

procedure TheIntVectorTest.TestReversed;
const N = 10;
var
  I, J: Integer;
  V: TItem;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
    J := I;
    for V in Vector.Reversed do begin
      AssertEquals('V', A[J], V);
      Dec(J);
    end;
  end;
end;

{ TheStrVectorTest }

function TheStrVectorTest.Item(const N: Integer): String;
begin
  Result := I2S(N);
end;

function TheStrVectorTest.Value(const It: String): Integer;
begin
  Result := S2I(It);
end;

procedure TheStrVectorTest.TestGetEnumerator;
const N = 10;
var
  I, J: Integer;
  V: TItem;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
    J := 0;
    for V in Vector do begin
      AssertEquals('V', A[J], V);
      Inc(J);
    end;
  end;
end;

procedure TheStrVectorTest.TestReversed;
const N = 10;
var
  I, J: Integer;
  V: TItem;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
    J := I;
    for V in Vector.Reversed do begin
      AssertEquals('V', A[J], V);
      Dec(J);
    end;
  end;
end;

{ TheIntCmpVectorTest }

function TheIntCmpVectorTest.Item(const N: Integer): Integer;
begin
  Result := N;
end;

function TheIntCmpVectorTest.Value(const It: Integer): Integer;
begin
  Result := It;
end;

procedure TheIntCmpVectorTest.TestGetEnumerator;
const N = 10;
var
  I, J: Integer;
  V: TItem;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
    J := 0;
    for V in Vector do begin
      AssertEquals('V', A[J], V);
      Inc(J);
    end;
  end;
end;

procedure TheIntCmpVectorTest.TestReversed;
const N = 10;
var
  I, J: Integer;
  V: TItem;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
    J := I;
    for V in Vector.Reversed do begin
      AssertEquals('V', A[J], V);
      Dec(J);
    end;
  end;
end;

{ TheStrCmpVectorTest }

function TheStrCmpVectorTest.Item(const N: Integer): String;
begin
  Result := I2S(N);
end;

function TheStrCmpVectorTest.Value(const It: String): Integer;
begin
  Result := S2I(It);
end;

procedure TheStrCmpVectorTest.TestGetEnumerator;
const N = 10;
var
  I, J: Integer;
  V: TItem;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
    J := 0;
    for V in Vector do begin
      AssertEquals('V', A[J], V);
      Inc(J);
    end;
  end;
end;

procedure TheStrCmpVectorTest.TestReversed;
const N = 10;
var
  I, J: Integer;
  V: TItem;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
    J := I;
    for V in Vector.Reversed do begin
      AssertEquals('V', A[J], V);
      Dec(J);
    end;
  end;
end;

{ TheInterfaceCmpVectorTest }

function TheInterfaceCmpVectorTest.Item(const N: Integer): IInterface;
begin
  Result := TInterfacedTracedObject.Create(nil, N);
end;

function TheInterfaceCmpVectorTest.Value(const It: IInterface): Integer;
begin
  Result := (It as TInterfacedTracedObject).ID;
end;

procedure TheInterfaceCmpVectorTest.TestGetEnumerator;
const N = 10;
var
  I, J: Integer;
  V: TItem;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
    J := 0;
    for V in Vector do begin
      AssertEquals('V', Value(A[J]), Value(V));
      Inc(J);
    end;
  end;
end;

procedure TheInterfaceCmpVectorTest.TestReversed;
const N = 10;
var
  I, J: Integer;
  V: TItem;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
    J := I;
    for V in Vector.Reversed do begin
      AssertEquals('V', Value(A[J]), Value(V));
      Dec(J);
    end;
  end;
end;

{ TheObjectVectorTest }

function TheObjectVectorTest.Item(const N: Integer): TObject;
begin
  if N = -1 then
    Exit(nil);
  Result := TTracedObject.Create(nil, N);
end;

function TheObjectVectorTest.Value(const It: TObject): Integer;
begin
  if It = nil then
    Exit(-1);
  Result := (It as TTracedObject).ID;
end;

procedure TheObjectVectorTest.Done(var It: TObject);
begin
  FreeAndNil(It);
end;

procedure TheObjectVectorTest.TestAdd1;
var
  V: TTestObjectVector;
  Tracer: TBits;
begin
  Tracer := TBits.Create;
  try
    V := TTestObjectVector.Create(nil, 0);
    try
      AssertEquals('.Add', 0, V.Add(TTracedObject.Create(Tracer, 0)));
      AssertEquals('.Count', 1, V.Count);
    finally
      V.Free;
    end;
    AssertFalse('Owned obj not freed, Tracer[0]', Tracer[0]);
  finally
    Tracer.Free;
  end;
end;

procedure TheObjectVectorTest.TestKill;
var
  V: TTestObjectVector;
  Tracer: TBits;
  Obj: TTracedObject;
begin
  Tracer := TBits.Create;
  try
    V := TTestObjectVector.Create(Tracer, 0);
    try
      Obj := TTracedObject.Create(Tracer, 1);
      try
        AssertTrue('V alive', Tracer[0]);
        AssertTrue('a) Obj alive', Tracer[1]);
        V.Kill;
        V := nil;
        AssertFalse('V dead', Tracer[0]);
        AssertTrue('b) Obj alive', Tracer[1]);
      finally
        Obj.Free;
      end;
    finally
      V.Free;
    end;
  finally
    Tracer.Free;
  end;
end;

procedure TheObjectVectorTest.TestWipe;
var
  V: TTestObjectVector;
  Tracer: TBits;
  Obj: TTracedObject;
begin
  Tracer := TBits.Create;
  try
    V := TTestObjectVector.Create(nil, 0);
    try
      Obj := TTracedObject.Create(Tracer, 1);
      try
        AssertTrue('a) Obj alive', Tracer[1]);
        V.Wipe;
        AssertEquals('.Count', 0, V.Count);
        AssertTrue('b) Obj alive', Tracer[1]);
      finally
        Obj.Free;
      end;
    finally
      V.Free;
    end;
  finally
    Tracer.Free;
  end;
end;

procedure TheObjectVectorTest.TestGetEnumerator;
const N = 10;
var
  I, J: Integer;
  V: TItem;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
    J := 0;
    for V in Vector do begin
      AssertEquals('V', Value(A[J]), Value(V));
      Inc(J);
    end;
  end;
end;

procedure TheObjectVectorTest.TestReversed;
const N = 10;
var
  I, J: Integer;
  V: TItem;
  A: array[0..N - 1] of TItem;
begin
  for I := 0 to N - 1 do begin
    A[I] := Item(I);
    Vector.Add(A[I]);
    J := I;
    for V in Vector.Reversed do begin
      AssertEquals('V', Value(A[J]), Value(V));
      Dec(J);
    end;
  end;
end;

initialization
  RegisterTests([
    TheIntVectorTest,
    TheStrVectorTest,
    TheIntCmpVectorTest,
    TheStrCmpVectorTest,
    TheInterfaceCmpVectorTest,
    TheObjectVectorTest
  ]);

end.

